package org.xteam.box2text;

import java.util.List;

import org.xteam.box2text.box.Box;
import org.xteam.box2text.box.HOVOperator;
import org.xteam.box2text.box.HOperator;
import org.xteam.box2text.box.HVOperator;
import org.xteam.box2text.box.IOperator;
import org.xteam.box2text.box.OperatorKinds;
import org.xteam.box2text.box.ROperator;
import org.xteam.box2text.box.SLOperator;
import org.xteam.box2text.box.TextBox;
import org.xteam.box2text.box.VOperator;
import org.xteam.box2text.box.WDOperator;
import org.xteam.box2text.box.util.BoxSwitch;

public class BoxLength extends BoxSwitch<Point> {

	private Frame frame;
	private OperatorKinds context;

	public BoxLength(Frame frame, OperatorKinds context) {
		this.frame = frame;
		this.context = context;
	}
	
	public Point caseTextBox(TextBox box) {
		return new Point(box.getContents().length(), 0);
	}
	
	public Point caseWDOperator(WDOperator box) {
		Point bp = listLength(box.getArguments());
		return new Point(bp.width(), 0);
	}
	
	public Point caseVOperator(VOperator box) {
		if (box.getArguments().isEmpty()) {
			return new Point(0,0);
		}
		return vlength(box.getArguments());
	}

	public Point vlength(List<Box> arguments) {
		Point bp = null;
		OperatorKinds save = context;
		context = OperatorKinds.V;
		for (Box arg : arguments) {
			if (bp == null) {
				bp =doSwitch(arg);
			} else {
				bp = bp.v(doSwitch(arg));
			}
		}
		context = save;
		return bp;
	}
	
	public Point caseHOperator(HOperator box) {
		if (box.getArguments().isEmpty()) {
			return new Point(0,0);
		}
		return hlength(box.getArguments(), frame.getHs(box.getOptions()));
	}

	private Point hlength(List<Box> arguments, int hs) {
		Point bp = null;
		OperatorKinds save = context;
		context = OperatorKinds.H;
		for (Box arg : arguments) {
			if (bp == null) {
				bp = doSwitch(arg);
			} else {
				bp = bp.h(doSwitch(arg), hs);
			}
		}
		context = save;
		return bp;
	}
	
	public Point caseHOVOperator(HOVOperator box) {
		if (box.getArguments().isEmpty()) {
			return new Point(0,0);
		}
		return hovlength(box.getArguments(), frame.getHs(box.getOptions()));
	}

	private Point hovlength(List<Box> arguments, int hs) {
		Point bp = null;
		OperatorKinds save = context;
		context = OperatorKinds.H;
		for (Box arg : arguments) {
			if (bp == null) {
				bp = doSwitch(arg);
			} else {
				bp = bp.hov(doSwitch(arg), hs);
			}
		}
		context = save;
		return bp;
	}
	
	public Point caseHVOperator(HVOperator box) {
		if (box.getArguments().isEmpty()) {
			return new Point(0,0);
		}
		return hvlength(box.getArguments(), frame.getHs(box.getOptions()));
	}
	
	private Point hvlength(List<Box> arguments, int hs) {
		Point bp = null;
		OperatorKinds save = context;
		context = OperatorKinds.H;
		for (Box arg : arguments) {
			Point bpn = doSwitch(arg);
			if (bp == null) {
				bp = bpn;
			} else {
				bp = bp.hv(bpn, hs);
			}
		}
		context = save;
		return bp;
	}
	
	public Point caseIOperator(IOperator box) {
		if (box.getArguments().isEmpty()) {
			return new Point(0,0);
		}
		Point bp = hlength(box.getArguments(), frame.getHs(box.getOptions()));
		if (context == OperatorKinds.V) {
			int is = frame.getIs(box.getOptions());
			return new Point(bp.width() + is, bp.height());
		}
		return bp;
	}
	
	public Point caseROperator(ROperator box) {
		throw new RuntimeException("R box length not implemented");
	}
	
	public Point caseSLOperator(SLOperator box) {
		throw new RuntimeException("SL box should be removed");
	}
	
	protected Point listLength(List<Box> boxes) {
		if (boxes.isEmpty())
			return new Point(0, 0);
		if (boxes.size() > 1)
			throw new RuntimeException("operator should only contain one element");
		return doSwitch(boxes.get(0));
	}

	public static Point length(Box b, Frame f, OperatorKinds context) {
		BoxLength bl = new BoxLength(f, context);
		return bl.doSwitch(b);
	}

}
